package com.yunli.mall.common.component;

import com.yunli.mall.common.annotation.ImplInject;
import com.yunli.mall.common.configuration.MallConfiguration;
import com.yunli.mall.common.exception.ErrorCode;
import com.yunli.mall.common.exception.MallRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.stream.Collectors;


/**
 * @author wangyue
 * @description 处理@ImplInject，根据配置文件中mall.serviceImplInject注入具体实现类
 * @date 2019-10-28 22:57
 */
@Slf4j
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ImplInjectProcessor implements BeanPostProcessor {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private MallConfiguration mallConfiguration;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // 反射获取属性,只有带@ImplInject注解的才自己注入
        Arrays.stream(bean.getClass().getDeclaredFields()).filter(field -> field.isAnnotationPresent(ImplInject.class) && field.getType().isInterface())
                .collect(Collectors.toList()).forEach(field -> this.handlerAutoInject(field, bean, beanName));
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 注入
     *
     * @param field
     * @param bean
     */
    private void handlerAutoInject(Field field, Object bean, String beanName) {
        field.setAccessible(true);
        if (!mallConfiguration.getServiceImplInject().containsKey(field.getName())) {
            log.error(ErrorCode.SERVICE_NAME_NOT_SPECIFIED.getMessage() + ":{}", field.getName());
            throw new MallRuntimeException(ErrorCode.SERVICE_NAME_NOT_SPECIFIED);
        }
        // 获取service name
        String serviceImpl = mallConfiguration.getServiceImplInject().get(field.getName());
        if (!StringUtils.hasText(serviceImpl)) {
            log.error(ErrorCode.SERVICE_IMPL_NOT_SPECIFIED.getMessage(), ":{}", field.getName());
            throw new MallRuntimeException(ErrorCode.SERVICE_IMPL_NOT_SPECIFIED);
        }
        //
        if (!applicationContext.containsBean(serviceImpl)) {
            log.error(ErrorCode.SERVICE_IMPL_NOT_FOUND + ":{}", serviceImpl);
            throw new MallRuntimeException(ErrorCode.SERVICE_IMPL_NOT_FOUND);
        }
        // 使用ApplicationContext获取实例,并用反射set进去
        try {
            field.set(bean, applicationContext.getBean(serviceImpl, field.getType()));
        } catch (IllegalAccessException e) {
            log.error("{}类@ImplInject注入{}失败", beanName, field.getName());
            throw new MallRuntimeException(ErrorCode.INTERNAL_ERROR);
        }
    }

}
